package solver

import (
	"log"
)

type NumType int

const (
	ROW NumType = iota
	COL
	BOX
	CELL
)

func (c *Cell) num(t NumType) int {
	switch t {
	case ROW:
		return c.rowNum
	case COL:
		return c.colNum
	case BOX:
		return c.boxNum
	default:
		return c.cellNum
	}
}

func (g *Grid) sumupCandsHouse(box *House, t NumType) (candsHouse [9]int) {
	candsHouse = [9]int{-1, -1, -1, -1, -1, -1, -1, -1, -1}
	for _, cell := range box.cells {
		if cell.solved() {
			continue
		}
		for ci, candidate := range cell.candidates {
			if !candidate || candsHouse[ci] == cell.num(t) {
				continue
			}
			if candsHouse[ci] == -1 {
				candsHouse[ci] = cell.num(t)
			} else {
				candsHouse[ci] = -2
			}
		}
	}
	return
}

func (g *Grid) clearupLineInPointing(value int, line House, butBoxNum int) bool {
	result := false
	for _, cell := range line.cells {
		if cell.solved() || cell.boxNum == butBoxNum {
			continue
		}
		if cell.removeCandidate(value) {
			result = true
		}
	}
	return result
}

func (g *Grid) rowPointingBox(boxNum int) (used bool) {
	candsRow := g.sumupCandsHouse(&g.boxes[boxNum], ROW)
	for cand, rowNum := range candsRow {
		if rowNum < 0 {
			continue
		}
		if g.clearupLineInPointing(cand+1, g.rows[rowNum], boxNum) {
			used = true
			log.Printf("b%vr%v %v %v", boxNum, rowNum, cand+1, "Row Pointing")
		}
	}
	return
}

func (g *Grid) colPointingBox(boxNum int) (used bool) {
	candsCol := g.sumupCandsHouse(&g.boxes[boxNum], COL)
	for cand, colNum := range candsCol {
		if colNum < 0 {
			continue
		}
		if g.clearupLineInPointing(cand+1, g.cols[colNum], boxNum) {
			used = true
			log.Printf("b%vc%v %v %v", boxNum, colNum, cand+1, "Col Pointing")
		}

	}
	return
}

func (g *Grid) Pointing() (used bool) {
	for boxNum := 0; boxNum < 9; boxNum++ {
		if g.rowPointingBox(boxNum) {
			used = true
		}
		if g.colPointingBox(boxNum) {
			used = true
		}
	}
	return
}

func (g *Grid) clearupBoxInClaiming(value int, box *House, butLineNum int, t NumType) (used bool) {
	for _, cell := range box.cells {
		if cell.solved() || cell.num(t) == butLineNum {
			continue
		}
		if cell.removeCandidate(value) {
			used = true
		}
	}
	return
}

func (g *Grid) claimingRow(row *House) (used bool) {
	candsBox := g.sumupCandsHouse(row, BOX)
	for cand, boxNum := range candsBox {
		if boxNum < 0 {
			continue
		}
		rowNum := row.cells[0].rowNum
		if g.clearupBoxInClaiming(cand+1, &g.boxes[boxNum], rowNum, ROW) {
			used = true
			log.Printf("b%vr%v %v %v", boxNum, rowNum, cand+1, "Row Claiming")
		}
	}
	return
}

func (g *Grid) claimingCol(col *House) (used bool) {
	candsBox := g.sumupCandsHouse(col, BOX)
	for cand, boxNum := range candsBox {
		if boxNum < 0 {
			continue
		}
		colNum := col.cells[0].colNum
		if g.clearupBoxInClaiming(cand+1, &g.boxes[boxNum], colNum, COL) {
			used = true
			log.Printf("b%vc%v %v %v", boxNum, colNum, cand+1, "Col Claiming")
		}
	}
	return
}

func (g *Grid) Claiming() (used bool) {
	for _, row := range g.rows {
		if g.claimingRow(&row) {
			used = true
		}
	}

	for _, col := range g.cols {
		if g.claimingCol(&col) {
			used = true
		}
	}
	return
}
